package db

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/zeromicro/go-zero/core/logx"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"payme3000/framework/errorx"
	"time"
)

func GetPostgresqlDsn(config *Config) string {
	return fmt.Sprintf(
		"host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
		config.Host,
		config.Username,
		config.Password,
		config.Db,
		config.Port,
	)
}

func CreateGormPostgresql(dbType Type, config *Config) error {
	gormLock.Lock()
	defer gormLock.Unlock()

	// 锁定后再检查一次
	if gormDb := gormMap[dbType]; gormDb != nil {
		return nil
	}

	postgresqlDb, err := sql.Open("pgx", GetPostgresqlDsn(config))
	if err != nil {
		return errorx.Throw(context.Background(), err)
	}

	postgresqlDb.SetMaxOpenConns(12)
	postgresqlDb.SetMaxIdleConns(12)
	postgresqlDb.SetConnMaxLifetime(1 * time.Hour)
	postgresqlDb.SetConnMaxIdleTime(30 * time.Minute)

	// 定期打印数据库连接池状态
	go func() {
		ticker := time.NewTicker(5 * time.Minute)
		defer ticker.Stop()

		lastWaitCount := int64(0)

		for {
			select {
			case <-ticker.C:
				stats := postgresqlDb.Stats()
				incrWaitCount := stats.WaitCount - lastWaitCount
				lastWaitCount = stats.WaitCount
				logx.Infof("postgresql db stats with dbType:%d, incrWaitCount:%d, stats:%+v", dbType, incrWaitCount, stats)
			}
		}
	}()

	if err := postgresqlDb.Ping(); err != nil {
		errorx.Catch(context.Background(), postgresqlDb.Close())
		return errorx.Throw(context.Background(), err)
	}

	gormDb, err := gorm.Open(
		postgres.New(postgres.Config{
			Conn:                 postgresqlDb,
			PreferSimpleProtocol: false,
		}),
		&gorm.Config{
			PrepareStmt:            false,
			SkipDefaultTransaction: true,
		},
	)
	if err != nil {
		errorx.Catch(context.Background(), postgresqlDb.Close())
		return errorx.Throw(context.Background(), err)
	}

	gormMap[dbType] = gormDb

	logx.Infof("create gorm postgresql done with dbType:%d, host:%s, db:%s", dbType, config.Host, config.Db)

	return nil
}
